import { Table } from '@builtIns';
import WebpackLicense from '@components/WebpackLicense';
import { ApiMeta } from '@components/ApiMeta.tsx';

<WebpackLicense from="https://webpack.docschina.org/plugins/copy-webpack-plugin/" />

# CopyRspackPlugin

<ApiMeta specific={['Rspack']} />

将已存在的单个文件或整个目录拷贝到产物目录。

```js
new rspack.CopyRspackPlugin(options);
```

## 示例

- 拷贝一个文件。如果文件不存在，则插件会抛出错误。

```ts title="rspack.config.mjs"
import { rspack } from '@rspack/core';

export default {
  entry: './src/index.js',
  plugins: [
    new rspack.CopyRspackPlugin({
      // `./src/file.txt` -> `./dist/file.txt`
      patterns: [{ from: 'src/file.txt' }],
    }),
  ],
};
```

- `patterns` 配置可以是一个字符串，或是一个包含多个对象的数组。

```ts title="rspack.config.mjs"
import { rspack } from '@rspack/core';

export default {
  entry: './src/index.js',
  plugins: [
    new rspack.CopyRspackPlugin({
      // 这等价于 `patterns: [{ from: 'src/file.txt' }]`
      patterns: 'src/file.txt',
    }),
  ],
};
```

- 拷贝一个目录。如果目录中没有文件，则插件会抛出错误。

```ts title="rspack.config.mjs"
import { rspack } from '@rspack/core';

export default {
  entry: './src/index.js',
  plugins: [
    new rspack.CopyRspackPlugin({
      // `./dir/**/*` -> `./dist`
      // 例如 `./dir/foo.txt` -> `./dist/foo.txt`
      patterns: [{ from: 'dir' }],
    }),
  ],
};
```

- 使用 glob pattern 来匹配并拷贝文件。

```ts title="rspack.config.mjs"
import { rspack } from '@rspack/core';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

export default {
  entry: './src/index.js',
  plugins: [
    new rspack.CopyRspackPlugin({
      // `./src/*.json` -> `./dist/*.json`
      // 例如 `./src/foo.json` -> `./dist/foo.json`
      patterns: [
        {
          from: '*.json',
          context: path.join(__dirname, 'src'),
        },
      ],
    }),
  ],
};
```

- 使用 `to` 指定目标路径。

```ts title="rspack.config.mjs"
import { rspack } from '@rspack/core';

export default {
  entry: './src/index.js',
  plugins: [
    new rspack.CopyRspackPlugin({
      // `./dir/**/*` -> `./dist/other-dir`
      // 例如 `./dir/foo.txt` -> `./dist/other-dir/foo.txt`
      patterns: [{ from: 'dir', to: 'other-dir' }],
    }),
  ],
};
```

## 选项

### from

- **类型：** `string`
- **默认值：** `undefined`

拷贝的源路径，可以是绝对路径、相对路径、glob pattern，可以是文件或目录。

若传入相对路径，则是相对于 [context](#context) 选项。

```js title="rspack.config.mjs"
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

export default {
  plugins: [
    new rspack.CopyRspackPlugin({
      patterns: [
        // 相对路径
        { from: 'relative/path/to/file.js' },
        { from: 'relative/path/to/dir' },
        // 绝对路径
        { from: path.resolve(__dirname, 'src', 'file.js') },
        { from: path.resolve(__dirname, 'src', 'dir') },
        // glob
        { from: 'dir/**/*' },
        // 如果绝对路径是 `glob`，我们用 forward slashes 替换 backslashes，
        // 因为只有 forward slashes 可以用于 `glob`
        {
          from: path.posix.join(
            path.resolve(__dirname, 'src').replace(/\\/g, '/'),
            '*.txt',
          ),
        },
      ],
    }),
  ],
};
```

### to

- **类型：**

```ts
type To =
  | string
  | ((pathData: { context: string; absoluteFilename?: string }) => string);
```

- **默认值：** [output.path](/config/output#outputpath)

拷贝的输出路径，可以是绝对路径、相对路径或者是 Rspack 的模版字符串，例如 `'[name].[hash][ext]'`。当不指定 `to` 时，则相当于是 Rspack 的 [output.path](/config/output#outputpath)。

```js title="rspack.config.mjs"
export default {
  plugins: [
    new rspack.CopyRspackPlugin({
      patterns: [
        {
          from: 'dir',
          to: 'relative/path/to/dest/',
        },
        {
          from: 'dir',
          to: '/absolute/path/to/dest/',
        },
        {
          from: 'dir',
          to: '[path][name].[contenthash][ext]',
        },
      ],
    }),
  ],
};
```

### context

- **类型：** `string`
- **默认值：** [context](/config/context)

`context` 是一个路径，它会被添加到 `from` 路径的前面，并从结果路径中移除。

```js title="rspack.config.mjs"
import { rspack } from '@rspack/core';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

export default {
  plugins: [
    new rspack.CopyRspackPlugin({
      // `./src/*.json` -> `./dist/*.json`
      patterns: [{ from: '*.json', context: path.join(__dirname, 'src') }],
    }),
  ],
};
```

`context` 可以是一个绝对路径或相对路径。如果它是一个相对路径，则基于 Rspack 的 [context](/config/context) 转换为绝对路径。

只有当 `from` 包含 glob 时，`context` 才应该被显式设置。否则，`context` 会根据 `from` 是文件还是目录来自动设置：

- 如果 `from` 是一个文件，则 `context` 是它的目录。输出路径将是文件名。
- 如果 `from` 是一个目录，则 `context` 等于 `from`。输出路径将是目录内容的相对路径（包括嵌套内容）。

### toType

- **类型：** `'dir' | 'file' | 'template'`
- **默认值：** `undefined`

指定 [to](#to) 的类型，可以是目录，文件或 Rspack 的模版名，若不指定则会自动推断。

自动推断的规则：

- `dir`：如果 `to` 没有扩展名，或以 `/` 结尾。
- `file`：如果 `to` 不是一个目录，并且不是一个模版。
- `template`：如果 `to` 包含一个 template pattern。

示例：

- `dir`:

```js title="rspack.config.mjs"
export default {
  plugins: [
    new rspack.CopyRspackPlugin({
      patterns: [
        {
          from: 'path/to/file.txt',
          to: 'directory/with/extension.ext',
          toType: 'dir',
        },
      ],
    }),
  ],
};
```

- `file`:

```js title="rspack.config.mjs"
export default {
  plugins: [
    new rspack.CopyRspackPlugin({
      patterns: [
        {
          from: 'path/to/file.txt',
          to: 'file/without/extension',
          toType: 'file',
        },
      ],
    }),
  ],
};
```

- `template`:

```js title="rspack.config.mjs"
export default {
  plugins: [
    new rspack.CopyRspackPlugin({
      patterns: [
        {
          from: 'src/',
          to: 'dest/[name].[contenthash][ext]',
          toType: 'template',
        },
      ],
    }),
  ],
};
```

### noErrorOnMissing

- **类型：** `boolean`
- **默认值：** `false`

当没有找到对应的文件或目录时，是否忽略错误。

```js title="rspack.config.mjs"
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));

export default {
  plugins: [
    new rspack.CopyRspackPlugin({
      patterns: [
        {
          from: path.resolve(__dirname, 'missing-file.txt'),
          noErrorOnMissing: true,
        },
      ],
    }),
  ],
};
```

### force

- **类型：** `boolean`
- **默认值：** `false`

当产物中已经有同名的文件时，是否覆写该文件。

```js title="rspack.config.mjs"
export default {
  plugins: [
    new rspack.CopyRspackPlugin({
      patterns: [{ from: 'file.txt', force: true }],
    }),
  ],
};
```

### priority

- **类型：** `number`
- **默认值：** `0`

指定同名文件的优先级。

当设置 [force](#force) 为 `true` 时，如果匹配到同样的文件，优先级高的会覆写优先级低的。

```js title="rspack.config.mjs"
export default {
  plugins: [
    new rspack.CopyRspackPlugin({
      patterns: [
        // 第一个被拷贝
        {
          from: 'dir-1/file.txt',
          to: 'newfile.txt',
          priority: 5,
        },
        // 第二个被拷贝，并覆盖 "dir-1/file.txt"
        {
          from: 'dir-2/file.txt',
          to: 'newfile.txt',
          force: true,
          priority: 10,
        },
      ],
    }),
  ],
};
```

### globOptions

- **类型：**

```ts
type GlobOptions = {
  // 是否大小写敏感
  // @default true
  caseSensitiveMatch?: boolean;
  // 是否匹配以 `.` 开头的文件
  // @default true
  dot?: boolean;
  // 忽略特定路径，可以是一个 glob 形式的字符串数组
  // @default undefined
  ignore?: string[];
};
```

- **默认值：** `undefined`

设置拷贝时 glob 匹配的选项。

```js title="rspack.config.mjs"
export default {
  plugins: [
    new rspack.CopyRspackPlugin({
      patterns: [
        {
          from: 'public/**/*',
          globOptions: {
            dot: false,
            caseSensitiveMatch: false,
            ignore: ['**/file.*', '**/ignored-directory/**'],
          },
        },
      ],
    }),
  ],
};
```

### transform

- **类型：**

```ts
type transform =
  | {
      transformer: (
        input: Buffer,
        absoluteFilename: string,
      ) => string | Buffer | Promise<string> | Promise<Buffer>;
    }
  | ((
      input: Buffer,
      absoluteFilename: string,
    ) => string | Buffer | Promise<string> | Promise<Buffer>);
```

- **默认值：** `undefined`

允许修改文件内容。

```js title="rspack.config.mjs"
export default {
  plugins: [
    new rspack.CopyRspackPlugin({
      patterns: [
        {
          from: 'src/*.png',
          // `content` 参数是一个 [`Buffer`](https://nodejs.org/api/buffer.html) 对象，
          // 可以使用 `content.toString()` 转换为字符串。
          // `absoluteFilename` 参数是一个字符串，是文件被拷贝的绝对路径。
          transform(content, absoluteFilename) {
            return optimize(content);
          },
        },
      ],
    }),
  ],
};
```

### copyPermissions

- **类型：** `boolean`
- **默认值：** `false`

是否将源文件的权限复制到目标文件。

```js title="rspack.config.mjs"
export default {
  plugins: [
    new rspack.CopyRspackPlugin({
      patterns: [
        {
          from: 'src/executable.sh',
          copyPermissions: true, // 保留可执行权限
        },
      ],
    }),
  ],
};
```

这对于复制可执行文件、脚本或任何权限重要的文件特别有用。当设置为 `true` 时，插件将尝试在目标文件上设置与源文件相同的权限。

### info

- **类型：**

```ts
type Info = {
  immutable?: boolean;
  // 是否为拷贝的文件跳过压缩
  // @default false
  minimized?: boolean;
  chunkHash?: string[];
  contentHash?: string[];
  development?: boolean;
  hotModuleReplacement?: boolean;
  related?: {
    sourceMap?: string;
  };
  version?: string;
};
```

- **默认值：** `undefined`

允许你为拷贝的文件添加一些 assets info，这些信息可能会影响构建流程中的某些行为。

例如，默认情况下，被拷贝的 JS、CSS 文件会经过 Rspack 的 [minimizer](/config/optimization#optimizationminimizer) 进行压缩，如果你希望被拷贝的文件跳过压缩，可以设置 `info.minimized` 为 `true`。

```js title="rspack.config.mjs"
export default {
  plugins: [
    new rspack.CopyRspackPlugin({
      patterns: [
        {
          from: 'src/file.js',
          info: { minimized: true },
        },
      ],
    }),
  ],
};
```
